package com.atguigu.gmall.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import com.atguigu.gmall.common.cache.GmallCache;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.product.mapper.SkuSaleAttrValueMapper;
import com.atguigu.gmall.product.model.*;
import com.atguigu.gmall.product.service.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class SkuManageServiceImpl implements SkuManageService {

    @Autowired
    private SkuInfoService skuInfoService;

    @Autowired
    private SkuImageService skuImageService;

    @Autowired
    private SkuAttrValueService skuAttrValueService;

    @Autowired
    private SkuSaleAttrValueService skuSaleAttrValueService;

    @Autowired
    private BaseAttrInfoService baseAttrInfoService;

    @Autowired
    private BaseAttrValueService baseAttrValueService;

    @Autowired
    private SpuSaleAttrValueService spuSaleAttrValueService;

    @Autowired
    private BaseSaleAttrService baseSaleAttrService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 保存商品SKU
     * 1.将sku商品基本信息封装为SkuInfo对象，存入sku_info表一条记录
     * 2.将选择商品多张sku图片封装SkuImage集合，批量存入sku_image表
     * 3.将选择多个平台属性以及值封装SkuAttrValue集合，批量存入sku_attr_value表
     * 4.将选择多个销售属性以及值封装SkuSaleAttrValue集合，批量存入sku_sale_attr_value表
     *
     * @param skuInfo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveSkuInfo(SkuInfo skuInfo) {
        //1.将sku商品基本信息封装为SkuInfo对象，存入sku_info表一条记录
        skuInfoService.save(skuInfo);
        Long skuId = skuInfo.getId();
        //2.将选择商品多张sku图片封装SkuImage集合，批量存入sku_image表
        List<SkuImage> skuImageList = skuInfo.getSkuImageList();
        if (!CollectionUtils.isEmpty(skuImageList)) {
            skuImageList.stream().forEach(skuImage -> {
                //关联Sku
                skuImage.setSkuId(skuId);
            });
            //批量保存商品sku图片
            skuImageService.saveBatch(skuImageList);
        }

        //3.将选择多个平台属性以及值封装SkuAttrValue集合，批量存入sku_attr_value表
        List<SkuAttrValue> skuAttrValueList = skuInfo.getSkuAttrValueList();
        if (!CollectionUtils.isEmpty(skuAttrValueList)) {
            skuAttrValueList.stream().forEach(skuAttrValue -> {
                //关联Sku
                skuAttrValue.setSkuId(skuId);
            });
            //批量保存sku平台属性
            skuAttrValueService.saveBatch(skuAttrValueList);
        }

        //4.将选择多个销售属性以及值封装SkuSaleAttrValue集合，批量存入sku_sale_attr_value表
        List<SkuSaleAttrValue> skuSaleAttrValueList = skuInfo.getSkuSaleAttrValueList();
        if (!CollectionUtils.isEmpty(skuSaleAttrValueList)) {
            skuSaleAttrValueList.stream().forEach(skuSaleAttrValue -> {
                //关联Spu
                skuSaleAttrValue.setSpuId(skuInfo.getSpuId());
                //关联Sku
                skuSaleAttrValue.setSkuId(skuId);
            });
            //批量保存sku销售属性值
            skuSaleAttrValueService.saveBatch(skuSaleAttrValueList);
        }
        RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(RedisConst.SKU_BLOOM_FILTER);
        bloomFilter.add(skuId);
    }

    @Override
    public Page<SkuInfo> getSkuByPage(Page<SkuInfo> infoPage, Long category3Id) {
        LambdaQueryWrapper<SkuInfo> queryWrapper = new LambdaQueryWrapper<>();
        if (category3Id != null) {
            queryWrapper.eq(SkuInfo::getCategory3Id, category3Id);
        }
        queryWrapper.orderByDesc(SkuInfo::getUpdateTime);
        return skuInfoService.page(infoPage, queryWrapper);
    }

    @Override
    public void onSale(Long skuId) {
        SkuInfo skuInfo = new SkuInfo(skuId);
        skuInfo.setIsSale(1);
        skuInfoService.updateById(skuInfo);

        //TODO 1.将商品信息放入分布式缓存  2.将上架商品存入全文检索搜索引擎ElasticSearch
    }

    @Override
    public void cancelSale(Long skuId) {
        //SQL : update sku_info set is_sale = 0 where id = ?
        //1.创建用于更新Wrapper
        LambdaUpdateWrapper<SkuInfo> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(SkuInfo::getIsSale, 0);
        updateWrapper.eq(SkuInfo::getId, skuId);
        skuInfoService.update(updateWrapper);

        //TODO 1.清理分布式缓存Redis或者修改商品信息  2.将下架商品从全文检索搜索引擎ElasticSearch删除
    }

    @Override
    public SkuInfo getSkuInfo(Long skuId) {

        //获取sku基本信息
        LambdaQueryWrapper<SkuInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SkuInfo::getId, skuId);
        SkuInfo skuInfo = skuInfoService.getOne(queryWrapper);
        List<SkuImage> skuImageList = skuImageService.getListBySkuId(skuId);
        if (!CollectionUtils.isEmpty(skuImageList))
            skuInfo.setSkuImageList(skuImageList);


        //获取sku_attr_value 信息
        List<SkuAttrValue> skuAttrValueList = skuAttrValueService.getListBySkuId(skuId);
        List<SkuAttrValue> skuAttrValues = skuAttrValueList.stream().peek(skuAttrValue -> {

            // 获取其中 base_attr_value value_id信息
            LambdaQueryWrapper<BaseAttrValue> baseAttrValueQueryWrapper = new LambdaQueryWrapper<>();
            baseAttrValueQueryWrapper.eq(BaseAttrValue::getId, skuAttrValue.getValueId());
            BaseAttrValue baseAttrValueOne = baseAttrValueService.getOne(baseAttrValueQueryWrapper);

            //获取base_attr_info attr_id
            LambdaQueryWrapper<BaseAttrInfo> baseAttrInfoQueryWrapper = new LambdaQueryWrapper<>();
            baseAttrInfoQueryWrapper.eq(BaseAttrInfo::getId, skuAttrValue.getAttrId());
            BaseAttrInfo baseAttrInfoOne = baseAttrInfoService.getOne(baseAttrInfoQueryWrapper);
            //获取到的信息保存到skuAttrValue中
            skuAttrValue.setAttrName(baseAttrInfoOne.getAttrName());
            skuAttrValue.setValueName(baseAttrValueOne.getValueName());

        }).collect(Collectors.toList());

        //获取颜色信息列表
        List<SkuSaleAttrValue> skuSaleAttrValueList = skuSaleAttrValueService.getListBySkuId(skuId);
        List<SkuSaleAttrValue> skuSaleAttrValues = skuSaleAttrValueList.stream().peek(skuSaleAttrValue -> {

            //获取spu_sale_attr_value中颜色值
            LambdaQueryWrapper<SpuSaleAttrValue> spuSaleAttrValueQueryWrapper = new LambdaQueryWrapper<>();
            spuSaleAttrValueQueryWrapper.eq(SpuSaleAttrValue::getId, skuSaleAttrValue.getSaleAttrValueId());
            SpuSaleAttrValue spuSaleAttrValue = spuSaleAttrValueService.getOne(spuSaleAttrValueQueryWrapper);

            //获取属性名称
            LambdaQueryWrapper<BaseSaleAttr> baseSaleAttrLambdaQueryWrapper = new LambdaQueryWrapper<>();
            baseSaleAttrLambdaQueryWrapper.eq(BaseSaleAttr::getId, spuSaleAttrValue.getBaseSaleAttrId());
            BaseSaleAttr one = baseSaleAttrService.getOne(baseSaleAttrLambdaQueryWrapper);

            //赋值AttrName
            //赋值ValueName
            skuSaleAttrValue.setSaleAttrName(one.getName());
            skuSaleAttrValue.setSaleAttrValueName(spuSaleAttrValue.getSaleAttrValueName());
            skuSaleAttrValue.setBaseSaleAttrId(spuSaleAttrValue.getBaseSaleAttrId());
            skuSaleAttrValue.setSaleAttrValueId(skuSaleAttrValue.getSaleAttrValueId());
        }).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(skuAttrValues))
            skuInfo.setSkuAttrValueList(skuAttrValues);
        if (!CollectionUtils.isEmpty(skuSaleAttrValues))
            skuInfo.setSkuSaleAttrValueList(skuSaleAttrValues);

        return skuInfo;
    }

    @Override
    public void updateSkuInfo(SkuInfo skuInfo) {
        Long skuId = skuInfo.getId();
        skuInfoService.saveOrUpdate(skuInfo);
        skuAttrValueService.removeById(skuId);
        skuSaleAttrValueService.removeById(skuId);
        skuImageService.removeById(skuId);
        List<SkuImage> skuImageList = skuInfo.getSkuImageList();
        if (!CollectionUtils.isEmpty(skuImageList)) {
            skuImageList.forEach(skuImage -> {
                //关联Sku
                skuImage.setSkuId(skuId);
            });
            //批量保存商品sku图片
            skuImageService.saveBatch(skuImageList);
        }

        //3.将选择多个平台属性以及值封装SkuAttrValue集合，批量存入sku_attr_value表
        List<SkuAttrValue> skuAttrValueList = skuInfo.getSkuAttrValueList();
        if (!CollectionUtils.isEmpty(skuAttrValueList)) {
            skuAttrValueList.forEach(skuAttrValue -> {
                //关联Sku
                skuAttrValue.setSkuId(skuId);
            });
            //批量保存sku平台属性
            skuAttrValueService.saveBatch(skuAttrValueList);
        }

        //4.将选择多个销售属性以及值封装SkuSaleAttrValue集合，批量存入sku_sale_attr_value表
        List<SkuSaleAttrValue> skuSaleAttrValueList = skuInfo.getSkuSaleAttrValueList();
        if (!CollectionUtils.isEmpty(skuSaleAttrValueList)) {
            skuSaleAttrValueList.forEach(skuSaleAttrValue -> {
                //关联Spu
                skuSaleAttrValue.setSpuId(skuInfo.getSpuId());
                //关联Sku
                skuSaleAttrValue.setSkuId(skuId);
            });
            //批量保存sku销售属性值
            skuSaleAttrValueService.saveBatch(skuSaleAttrValueList);
        }
    }

    @Override
    public SkuInfo getSkuInfoImage(Long skuId) {
        try {
            //1.优先从分布式缓存Redis获取业务数据
            //1.1 拼接业务数据Key 形式 "sku:商品ID skuId:info"
            String dataKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKUKEY_SUFFIX;
            //1.2 获取缓存中数据
            SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(dataKey);
            //2.命中缓存则直接响应业务数据
            if (skuInfo != null) {
                return skuInfo;
            }
            //3.未命中缓存则调用尝试获取分布式锁
            //3.1 拼接锁Key 形式 "sku:商品ID skuId:lock"
            String lockKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKULOCK_SUFFIX;
            //3.2 获取锁对象
            RLock lock = redissonClient.getLock(lockKey);
            //3.3 尝试获取分布锁
            boolean flag = lock.tryLock(RedisConst.SKULOCK_EXPIRE_PX2, RedisConst.SKULOCK_EXPIRE_PX1, TimeUnit.SECONDS);
            //5.获取锁成功 执行查库业务
            if (flag) {
                try {
                    skuInfo = this.getSkuInfoFromDB(skuId);
                    if (skuInfo != null) {
                        //查询数据库有值 将查询结果放入分布式缓存
                        redisTemplate.opsForValue().set(dataKey, skuInfo, RedisConst.SKUKEY_TIMEOUT, TimeUnit.SECONDS);
                        return skuInfo;
                    } else {
                        //查询数据库为空 出现缓存穿透
                        redisTemplate.opsForValue().set(dataKey, skuInfo, 5, TimeUnit.MINUTES);
                        return skuInfo;
                    }
                } finally {
                    //释放锁
                    lock.unlock();
                }
            } else {
                //6.获取锁失败 自旋(保证用户请求线程一定能获取到业务数据)
                TimeUnit.MILLISECONDS.sleep(500);
                return this.getSkuInfo(skuId);
            }
        } catch (Exception e) {
            //6.当锁服务不可以,兜底处理方案查询数据库
            log.error("[商品服务]获取商品信息锁服务不可用,兜底处理查库:{}", e);
            return this.getSkuInfoFromDB(skuId);
        }
    }


    @Override
    @GmallCache(prefix = "attrList:")
    public List<BaseAttrInfo> getAttrList(Long skuId) {

        return skuAttrValueService.getAttrList(skuId);
    }

    @Override
    @GmallCache(prefix = "skuValueIdsMap:")
    public String getSkuValueIdsMap(Long spuId) {
        SkuSaleAttrValueMapper skuSaleAttrValueMapper = (SkuSaleAttrValueMapper) skuSaleAttrValueService.getBaseMapper();
        //1.调用动态SQL得到多条 销售属性跟SKUID对照关系
        List<Map> list = skuSaleAttrValueMapper.getSkuValueIdsMap(spuId);
        //2.遍历List将所有销售属性ID作为jsonKey 商品SKUID作为val
        HashMap<Object, Object> mapResult  = new HashMap<>();
        for (Map map : list) {
            Object value_ids = map.get("value_ids");
            Object skuId = map.get("sku_id");
            mapResult.put(value_ids, skuId);
        }
        return JSON.toJSONString(mapResult);
    }

    @Override
    public BigDecimal getSkuPrice(Long skuId) {

        LambdaQueryWrapper<SkuInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SkuInfo::getId, skuId);
        queryWrapper.select(SkuInfo::getPrice);
        SkuInfo skuInfo = skuInfoService.getOne(queryWrapper);
        if (skuInfo != null) {
            return skuInfo.getPrice();
        }
        return new BigDecimal("0.0");

    }

    @Override
    @GmallCache(prefix = RedisConst.SKUKEY_PREFIX, suffix = RedisConst.SKUKEY_SUFFIX)
    public SkuInfo getSkuInfoFromDB(Long skuId) {
        //1.先根据ID查询商品对象
        SkuInfo skuInfo = skuInfoService.getById(skuId);
        //2.根据SkuID查询商品图片列表
        if (skuInfo != null) {
            LambdaQueryWrapper<SkuImage> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(SkuImage::getSkuId, skuId);
            List<SkuImage> skuImageList = skuImageService.list(queryWrapper);
            skuInfo.setSkuImageList(skuImageList);
        }
        return skuInfo;
    }

}
